// todo: put scaler after max/min so we can get super long decays and such.
// todo: fix recording feature when SL is decoupled

BaseControl{
	
	
	var <>value = 0, <>min = 0, <>max = 1, midiNum, <>guiObj, <>loopLengthGui, <>randAmtGui, parent;
	var <>loop, <>mode = 0,  <>autoLoopBeat, <>autoBeatsPerLoop, <>lastValue, <>log, <>randAmt = 0;
	var <>group, scaler, <>master, <>lastReturned, lastReturnedS, pos, posrand = false, <>extFun, <>extSetFun, <>name, plugins, postLaunch = false;
	var logger;
	var <default;
	
	classvar <>children, <>lastTouched;


	*new{ |name, default = 0, min = 0, max = 1, midiNum = 0, theLog = 1, postLaunch = false| 
		var o;
		
		if(
			children == nil,
			
			{children = Bag.new};
		
		);
		
		o = super.new;
		o.init(name, default, min, max, midiNum, theLog, postLaunch);
		^o;
	}


	init{ |nameArg, defaultArg, minArg, maxArg, midiNumArg, theLogArg, postLaunchArg|
	
		logger = RippleLogger.new;
		logger.fileName = "RippleControl";
	
		name = nameArg;
		default = defaultArg; 
		min = minArg;
		max = maxArg; 
		midiNum = midiNumArg; 
		log = theLogArg;
		postLaunch = postLaunchArg;
		
		
		
		children.add(this);
		
		//scaler = Scaler.new;
		
		loop = Array.new(1000);
		1000.do{ loop.add(0) };
		
		postLaunch = postLaunchArg;
		
		autoBeatsPerLoop = 64;
		
		plugins = List.new;
		
		this.onInit;
		
		
	}
	
	onInit{
		
	}
	
	setValue{|val|
		value = val;
	}
	
	getValue{
		^value;
	}
	
	setParent{|parArg|
		parent = parArg;
	}
	
	setInitialValue{|val|
		this.setValue(val)
	}
	
	setRandValue{} // shouldn't be called all the time, but is
	
	midiNum{} // get rid of this when possible
	
	getOneToZeroValue{
		//^lastValue; // no idea why this was set to lastValue
		^value;
	}
	
	getScaler{
		^scaler;
	}
	
	getRandAmt{
		^randAmt;
	}
	
	getParent{
		^parent;
	}
	
	getPlugins{
		^plugins;
	}
	
	hasPlugins{
		^(plugins.notNil && plugins.size > 0);	
	}	
	
	accept{|xb|
		xb.forControl(this);
		plugins.do{|plugin|
			var pluginXb;
			pluginXb = XMLNodeBuilder.new;
			pluginXb.setParent(xb.getNode);
			plugin.accept(pluginXb);
		}
	}
	
	getName{
		^name;
	}
	
}


RippleControl : BaseControl{
	
	
	displayPluginGui{
		ControlPluginsGui.new(this);
	}
	
	addPlugin{|plugin|
		plugins.add(plugin);
		this.refreshPluginButton;
	}
	
	removePlugin{|plugin|
		plugins.remove(plugin);
		this.refreshPluginButton;
	}
	
	refreshPluginButton{
		parent.setControlGuiValue(this.name, this.hasPlugins, \PluginButton);
	}
	
	
	getBeat{ // refactor
		
		//autoLoopBeat = parent.beats % autoBeatsPerLoop;
		//^autoLoopBeat;
		^0
	
	}
	
	
	setRandValue{
	
		arg theValue;
		randAmt = theValue;
		
		this.notifyParentOfNewValue( (name ++ "RandAmt"), theValue);
	
	}
	
	setLoopValue{
		arg theValue;
		autoBeatsPerLoop = theValue;
		

		this.notifyParentOfNewValue( (name ++ "LoopLength"), theValue);
	}
	
	setValue{
		arg theValue;
		this.baseSetValue(theValue);		
		
		// if registered as remote controller, there can be conflicts
		if(this != RemoteControl.remoteController){
			//Sequencer.lastControlSet = this;
		};
		
	}
	
	secretSetValue{
		arg theValue;
		this.baseSetValue(theValue);
		name.postln;
		theValue.postln;
	}
	
	baseSetValue{ 
		arg theValue;
		var incValue;
			
		if(extSetFun != nil){
			extSetFun.value(theValue);
		};
		
		
		if(SCWindow.allWindows.size == 3){
			mode = 0;
		};
		
		
		if(((theValue == "inc") || (theValue == "dec")),{
			incValue = (max-min) * 0.01;
			
			
			
			if(theValue == "inc"){
				value = min(
						value + incValue,
						max
					);
			};
			if(theValue == "dec"){
				value = max(
						value - incValue,
						min
					);
			
			};
		},{
			value = theValue;
		});
		
		
		
		lastValue = value;
		
		this.notifyParentOfNewValue(name, theValue);
		
		if(mode == 0){
			loop.put(this.getBeat.value, theValue);
		};
		
		
		if(postLaunch){
			parent.setValPostLaunch(name, this.getValue);
		};
		
	}
	
	setInitialValue{|val|
		value = (val - min)/(max-min);	
		//value = val;
		lastValue = value;
	}
	
	//for some, set the value on the server for immediate results
	setValsOnServer{				
		//if(name == \volume2 && parent != nil && parent.group){
		//	parent.group.set( name, this.getValue );
		//};		
	}
	
	notifyParentOfNewValue{|name, theValue|
		parent.recieveControlChange(name, theValue);
	}
	
	
	localSetValue{
	
	
		arg theValue;
		
		value = theValue;
		
		//if(log == true){
		//	value = theValue ** 2;
		//};
		
		lastValue = value;
		
		this.notifyParentOfNewValue(name, theValue);
		
		if(mode == 0){
			loop.put(this.getBeat.value, theValue);
		}	
	
	
	}
	
	midiSetValue{
	
		arg midiValue;
		var sliderValue;
		
		sliderValue = midiValue / 128;
		
		this.setValue(sliderValue);
	
	}
	
	
	// scaled/plugin'd last returned
	getLastReturnedS{
		if(lastReturnedS.isNil){
			^this.getValue;
		};	
		^lastReturnedS;
	}
	
	getLastReturned{
		^lastReturned;
	}
	
	descaleValue{|val|
		^((val - min )/(max-min)) ** (1/log);
	}
	
	emancipate{
		this.master = nil;
	}
	
	getValue{

		var r, scaleVal, rand;
		
		if(mode == 1){
			
			lastTouched = this;
			this.localSetValue( loop.at(this.getBeat.value) );
		};
		
		if(mode == 0){
			this.localSetValue(lastValue);
		};
		
		// values are all between 0 and 1 now, so deal with masterizing here
		if(
			master != nil,
			{r = master.lastReturned;},
			{
				r = value;	
				r = r + randAmt.rand2;
			}
		
		);
		
		r = r.min(1).max(0);		
		
		if(extFun != nil){
			try{
				r = this.extFun.value(r);
			}{|e|
				("++++++++++++++++++++ an error happend while running extfun in " + name + " +++++++++++++++++++++").postln;
				("ERROR: " + e).postln;
			};
		};
		
		
		lastReturned = r;
		
		r =  r  ** log;	
		
		
		
		// now deal with scaling 
		//r = scaler.scale(r) ;
		
		
		
		r = 	( ( (max - min) * r ) + min );
		
		
		r = r.min(max).max(min);
		
		
		r = this.processPlugins(r);
		
		lastReturnedS = r;
		
		this.sendFeedback(this.descaleValue(r)) //send feedback to parent, in case we're displaying it in associated view
	
		^r;
	
	}
	
	sendFeedback{|val|
		parent.setControlGuiValue(name, val, \Feedback);
	}
	
	processPlugins{|r|
		plugins.do{|plugin|
		
		
				r = plugin.process(r);
		/*
			try{
				r = plugin.process(r);
			}{|e|
				("An error occured while processing plugins for: " + this.name).post;
				(". Plugin class: " + plugin.class ).postln;
				e.reportError;
			};
		*/
		
		};
		^r;
	}
	
	posser{
	
		if(
			(pos != nil),
			{
				if(
					posrand,
					{pos = pos.scramble},
					{pos = pos.rotate(1)}	
				);
				
				^pos.at(0) * 0.1;
			},
			{^1;}
		)	
	}
	 
	pos{
	
		arg arr, posrandArg;
	
		pos = arr;
		if( posrandArg == \rand){
			posrand = true;	
		}
	
	}
	
	resetPlugins{
		plugins.do{|plugin|
			plugin.reset;
		}
	}

		
	getGuiClass{
		^DefaultControlView;
	}
	
	
}

FloatDataControl : RippleControl{	

}


